#include "command.h"
#include "print.h"
#include "stringtool.h"

template <typename... Args>
void print_error(Args &&...args)
{
    chainsaw::print_class_error("command", std::forward<Args>(args)...);
}

chainsaw::command::command(std::string s)
{
    stringtool tool(s);
    std::vector<int> indices = tool.find_all(' ');

    std::vector<std::string> bins;

    if (indices.empty())
    {
        bins.push_back(tool());
    }
    else
    {
        bins = tool.split(indices);
    }
    check(bins);
    parse(bins);
}

chainsaw::command::command(int argc, char **argv)
{
    std::vector<std::string> bins;
    for (int i = 1; i < argc; ++i)
    {
        bins.push_back(argv[i]);
    }
    check(bins);
    parse(bins);
}

bool chainsaw::command::find(std::string option) const
{
    bool res = false;
    for (auto n : m_all)
    {
        if (n.value == option)
        {
            res = true;
            break;
        }
    }
    return res;
}

std::vector<std::string> chainsaw::command::get_option_arguments(std::string option) const
{
    std::vector<std::string> res;
    for (auto n : m_all)
    {
        if (n.value == option)
        {
            res = n.args;
            break;
        }
    }

    return res;
}

void chainsaw::command::print() const
{
    info_nowrap("command print:{", m_all.size(), "}:[");

    for (int i = 0; i < m_all.size(); ++i)
    {
        auto t = m_all[i];
        raw(t.value);

        if (!t.args.empty())
        {
            raw("(=)");

            auto args = t.args;
            for (int j = 0; j < args.size(); ++j)
            {
                raw(args[j]);
                if (j != args.size() - 1)
                {
                    raw(",");
                }
            }
        }
        if (i != m_all.size() - 1)
            raw(";");
    }
    raw("]");
    raw_endl();
}

enum class Mode
{
    DASH_ONE,
    DASH_TWO,
    DEFAULT
};

void chainsaw::command::check(std::vector<std::string> const &data) const
{
    Mode mode = Mode::DEFAULT;
    auto try_again = [this](std::string s)
    {
        print_error("error para: [", s, "]");
        print_error("try again!");
        exit(0);
    };
    for (auto t : data)
    {
        stringtool tool(t);
        if (tool.startsWith("--"))
        {
            mode = Mode::DASH_TWO;
        }
        else if (tool.startsWith("-"))
        {
            mode = Mode::DASH_ONE;

            if (~tool.find("="))
            {
                print_error("usage: -<alpha>");
                try_again(tool());
            }
        }
        else
        {
            if (mode == Mode::DEFAULT)
            {
                print_error("usage: -<alpha> <value>");
                try_again(tool());
            }
        }

        if (tool.startsWith("=") || tool.endsWith("="))
        {
            print_error("usage: --<word>=<value> or --<word> <value>");
            try_again(tool());
        }
    }
}

void clear_node(chainsaw::command::Node *node);

bool is_node_empty(chainsaw::command::Node *node);

void chainsaw::command::parse(std::vector<std::string> const &data)
{
    Node node;
    Mode mode = Mode::DEFAULT;

    for (auto t : data)
    {
        stringtool tool(t);
        if (tool.startsWith("--"))
        {
            if (mode != Mode::DEFAULT)
            {
                m_all.push_back(node);
                clear_node(&node);
            }
            mode = Mode::DASH_TWO;
            int pos = tool.find('=');
            if (~pos)
            {
                node.value = tool.substr(2, pos);
                node.args.push_back(tool.substr(pos + 1, tool.size()));
            }
            else
            {
                node.value = tool.substr(2, tool.size());
            }
        }
        else if (tool.startsWith("-"))
        {
            if (mode != Mode::DEFAULT)
            {
                m_all.push_back(node);
                clear_node(&node);
            }
            mode = Mode::DASH_ONE;
            if (tool.size() >= 3)
            {
                int n = tool.size() - 1;
                int i = 1;
                while (i != n)
                {
                    node.value = tool[i];
                    m_all.push_back(node);
                    clear_node(&node);

                    ++i;
                }
            }
            node.value = tool.tail();
        }
        else
        {
            node.args.push_back(tool());
        }
    }

    if (!is_node_empty(&node))
    {
        m_all.push_back(node);
    }
}

void clear_node(chainsaw::command::Node *node)
{
    node->value.clear();
    node->args.clear();
}

bool is_node_empty(chainsaw::command::Node *node)
{
    return node->value.empty() && node->args.empty();
}
